import java.io.*;
import java.net.*;
import java.util.*;

/**
 * 聊天服务器类
 * 这是一个简单的聊天服务器实现，可以接收客户端连接并处理消息
 * 使用了多线程技术，每个客户端连接都会创建一个新的线程来处理
 * 
 * 网络编程基础概念：
 * 1. Socket（套接字）：网络通信的端点，用于在不同计算机之间建立连接和传输数据
 * 2. ServerSocket：服务器端套接字，用于监听客户端的连接请求
 * 3. 多线程：允许服务器同时处理多个客户端连接
 * 4. 输入输出流：用于在网络中传输数据
 */
public class ChatServer {
    // 服务器套接字，用于监听客户端连接请求
    // ServerSocket是Java提供的服务器端套接字类，负责监听指定端口上的连接请求
    private ServerSocket serverSocket;
    
    // 服务器运行状态标志，true表示服务器正在运行，false表示已停止
    // 这个布尔变量用于控制服务器的主循环是否继续运行
    private boolean isRunning;
    
    // 客户端处理器列表，用于管理所有已连接的客户端
    // 使用ArrayList集合存储所有客户端处理器，便于统一管理和广播消息
    private List<ClientHandler> clients;
    
    /**
     * 构造函数，初始化聊天服务器
     * 构造函数是一种特殊的方法，当创建类的新实例时会自动调用
     * @param port 服务器监听的端口号，客户端需要连接到这个端口才能与服务器通信
     *             端口号范围是0-65535，其中0-1023是系统保留端口
     */
    public ChatServer(int port) {
        try {
            // 创建服务器套接字，绑定到指定端口
            // ServerSocket的构造函数会尝试在指定端口上创建监听套接字
            // 如果端口被占用或其他网络问题，会抛出IOException异常
            serverSocket = new ServerSocket(port);
            
            // 设置服务器运行状态为true，表示服务器正在运行
            // 这个标志用于控制服务器主循环的执行
            isRunning = true;
            
            // 初始化客户端处理器列表
            // 创建一个ArrayList来存储所有连接到服务器的客户端处理器
            // ArrayList是一个动态数组，可以根据需要自动扩容
            clients = new ArrayList<>();
            
            // 输出服务器启动信息
            // System.out.println是Java中用于向控制台输出信息的方法
            // 这里告诉用户服务器已经启动并监听在哪个端口上
            System.out.println("服务器启动，监听端口: " + port);
        } catch (Exception e) {
            // 异常处理：如果服务器启动失败，这里应该输出错误信息
            // 但在当前代码中没有具体处理
            // Exception是所有异常的父类，可以捕获各种可能的异常
            System.out.println("服务器启动失败: " + e.getMessage());
        }
    }
    
    /**
     * 启动服务器，开始接受客户端连接
     * 这是服务器的主循环，会一直运行直到服务器被停止
     * 主循环是服务器的核心部分，负责持续监听和处理客户端连接
     */
    public void start() {
        try {
            // 服务器主循环，只要服务器处于运行状态就继续执行
            // while循环会一直执行，直到isRunning变量变为false
            // 这使得服务器能够持续监听客户端连接请求
            while (isRunning) {
                // 输出等待客户端连接的信息
                // 这条信息会在每次循环开始时显示，表明服务器正在等待连接
                System.out.println("等待客户端连接");
                
                // 等待并接受客户端连接请求，这是一个阻塞方法
                // 当有客户端连接时，会返回该客户端的Socket对象
                // ServerSocket.accept()方法会阻塞（暂停）程序执行，直到有客户端连接
                // 一旦有客户端连接，它会返回一个Socket对象，用于与该客户端通信
                Socket clientSocket = serverSocket.accept();
                
                // 输出新客户端连接的信息
                // 当有新客户端连接时，显示这条信息
                System.out.println("新客户端连接");
                
                // 为新连接的客户端创建一个处理器
                // ClientHandler是一个内部类，专门负责处理单个客户端的通信
                // this关键字表示当前ChatServer实例，将其传递给ClientHandler
                ClientHandler clientHandler = new ClientHandler(clientSocket, this);
                
                // 将客户端处理器添加到客户端列表中
                // add方法将新的客户端处理器添加到clients列表的末尾
                // 这样服务器就能跟踪所有已连接的客户端
                clients.add(clientHandler);
                
                // 为每个客户端创建一个新线程并启动，实现多客户端并发处理
                // Thread类用于创建和管理线程
                // new Thread(clientHandler).start()创建一个新线程并立即启动它
                // 这样每个客户端都在自己的线程中运行，不会相互阻塞
                new Thread(clientHandler).start();
            }
        } catch (Exception e) {
            // 异常处理：如果接受客户端连接时发生错误
            // 但在当前代码中没有具体处理
            System.out.println("接受客户端连接时发生错误: " + e.getMessage());
        }
        
        // 当服务器停止时，调用停止服务器方法清理资源
        // 这确保了即使发生异常，服务器也能正确地关闭和清理资源
        stopServer();
    }
    
    /**
     * 从客户端列表中移除指定的客户端
     * 当客户端断开连接时调用此方法
     * @param client 需要移除的客户端处理器
     *               这是之前添加到clients列表中的ClientHandler实例
     */
    public void removeClient(ClientHandler client) {
        // 从客户端列表中移除指定的客户端
        // remove方法会从ArrayList中删除指定的对象
        // 这确保了服务器不会再尝试向已断开的客户端发送消息
        clients.remove(client);
        
        // 输出客户端断开连接的信息
        // 通知服务器管理员有客户端断开了连接
        System.out.println("客户端断开连接");
    }
    
    /**
     * 广播消息给所有客户端
     * 在这个简单实现中，服务器将收到的消息进行反转后发送回客户端
     * @param message 需要广播的消息
     *                这是从某个客户端接收到的字符串消息
     */
    public void broadcastMessage(String message) {
        // 输出服务器收到消息的信息
        // 在控制台显示服务器收到了来自客户端的消息
        System.out.println("服务器收到消息: " + message);
        
        // 检查是否收到退出指令
        // equals方法用于比较两个字符串是否相等
        // 如果客户端发送"quit"消息，服务器将停止运行
        if ("quit".equals(message)) {
            // 输出收到退出指令的信息
            // 通知服务器管理员收到了退出指令
            System.out.println("收到退出指令");
            
            // 设置服务器运行状态为false，准备停止服务器
            // 这将导致主循环结束，服务器停止接受新连接
            isRunning = false;
            
            // 调用停止服务器方法
            // 这会清理所有已连接的客户端并关闭服务器套接字
            stopServer();
            
            // 直接返回，不再执行后续代码
            // return语句立即结束当前方法的执行
            return;
        }
        
        // 将收到的消息进行反转处理
        // StringBuilder是一个可变的字符串类，用于高效地构建字符串
        // reverse()方法将字符串中的字符顺序颠倒
        // toString()方法将StringBuilder转换为普通的String
        String reversedMessage = new StringBuilder(message).reverse().toString();
        
        // 输出反转后的消息
        // 在控制台显示处理后的消息
        System.out.println("反转后的消息: " + reversedMessage);
    }
    
    /**
     * 停止服务器，关闭所有相关资源
     * 当服务器需要关闭时调用此方法
     * 资源管理是网络编程中的重要概念，需要正确关闭所有打开的资源
     */
    public void stopServer() {
        // 设置服务器运行状态为false
        // 确保即使在异常情况下，服务器也会停止运行
        isRunning = false;
        
        try {
            // 检查服务器套接字是否存在且未关闭
            // serverSocket != null 确保serverSocket对象已经被创建
            // !serverSocket.isClosed() 确保套接字还没有被关闭
            if (serverSocket != null && !serverSocket.isClosed()) {
                // 关闭服务器套接字
                // close()方法会释放与套接字关联的所有资源
                // 这会停止服务器监听新的连接请求
                serverSocket.close();
            }
            
            // 遍历所有客户端处理器，关闭它们的连接
            // for-each循环遍历clients列表中的每个ClientHandler对象
            for (ClientHandler client : clients) {
                // 调用客户端处理器的关闭方法
                // 这会关闭与每个客户端的连接
                client.close();
            }
            
            // 清空客户端列表
            // clear()方法会移除列表中的所有元素
            // 这确保了不会保留对已关闭连接的引用
            clients.clear();
            
            // 输出服务器已关闭的信息
            // 通知服务器管理员服务器已经完全关闭
            System.out.println("服务器已关闭");
        } catch (Exception e) {
            // 异常处理：如果关闭服务器时发生错误
            // 但在当前代码中没有具体处理
            System.out.println("关闭服务器时发生错误: " + e.getMessage());
        }
    }
    
    /**
     * 程序入口点
     * main方法是Java程序的入口点，JVM会从这里开始执行程序
     * @param args 命令行参数，可以从命令行传递给程序的参数
     *             在这个程序中没有使用这些参数
     */
    public static void main(String[] args) {
        // 创建聊天服务器实例，监听8989端口
        // new关键字用于创建ChatServer类的新实例
        // 8989是传递给构造函数的端口号参数
        ChatServer server = new ChatServer(8989);
        
        // 启动服务器
        // 调用start方法开始服务器的主循环
        // 服务器将开始监听客户端连接并处理消息
        server.start();
    }
    
    /**
     * 客户端处理器内部类
     * 用于处理单个客户端的连接和消息
     * 实现了Runnable接口，可以作为线程执行
     * 内部类可以访问外部类的所有成员，便于服务器和客户端处理器之间的通信
     */
    static class ClientHandler implements Runnable {
        // 客户端套接字，用于与客户端通信
        // Socket对象代表与特定客户端的网络连接
        private Socket clientSocket;
        
        // 指向所属服务器的引用，用于调用服务器方法
        // 通过这个引用，客户端处理器可以调用ChatServer类的方法
        private ChatServer server;
        
        // 对象输入流，用于从客户端读取对象数据
        // ObjectInputStream可以读取序列化的Java对象
        private ObjectInputStream input;
        
        // 对象输出流，用于向客户端发送对象数据
        // ObjectOutputStream可以发送序列化的Java对象
        private ObjectOutputStream output;
        
        // 客户端连接状态标志，true表示已连接，false表示已断开
        // 这个布尔变量用于控制客户端处理循环是否继续执行
        private boolean isConnected;
        
        /**
         * 客户端处理器构造函数
         * @param socket 客户端套接字，代表与客户端的网络连接
         * @param server 所属服务器引用，用于调用服务器方法
         */
        public ClientHandler(Socket socket, ChatServer server) {
            // 保存客户端套接字引用
            // this.clientSocket引用当前对象的clientSocket字段
            // socket参数是传递进来的客户端套接字
            this.clientSocket = socket;
            
            // 保存服务器引用
            // this.server引用当前对象的server字段
            // server参数是外部ChatServer实例的引用
            this.server = server;
            
            // 初始化连接状态为已连接
            // 当创建ClientHandler时，默认认为连接是活动的
            this.isConnected = true;
            
            try {
                // 创建对象输出流，用于向客户端发送数据
                // ObjectOutputStream的构造函数需要一个OutputStream作为参数
                // clientSocket.getOutputStream()返回与客户端套接字关联的输出流
                output = new ObjectOutputStream(clientSocket.getOutputStream());
                
                // 创建对象输入流，用于从客户端读取数据
                // ObjectInputStream的构造函数需要一个InputStream作为参数
                // clientSocket.getInputStream()返回与客户端套接字关联的输入流
                input = new ObjectInputStream(clientSocket.getInputStream());
            } catch (Exception e) {
                // 异常处理：如果创建输入输出流时发生错误
                // 但在当前代码中没有具体处理
                System.out.println("创建输入输出流时发生错误: " + e.getMessage());
            }
        }
        
        /**
         * 线程执行方法
         * 这是客户端处理器的核心逻辑，运行在独立线程中
         * Runnable接口要求实现run方法，这是线程启动时执行的代码
         */
        @Override
        public void run() {
            try {
                // 客户端处理主循环，只要连接未断开就继续执行
                // while循环会持续运行，直到isConnected变为false
                // 这允许客户端处理器持续接收和处理来自客户端的消息
                while (isConnected) {
                    try {
                        // 从客户端读取对象数据，这是一个阻塞方法
                        // readObject()方法会阻塞直到从客户端接收到数据
                        // 它返回一个Object类型的对象，需要进行类型转换
                        Object obj = input.readObject();
                        
                        // 检查读取到的对象是否是字符串类型
                        // instanceof操作符用于检查对象是否是指定类型的实例
                        // 这确保我们只处理字符串消息
                        if (obj instanceof String) {
                            // 将对象转换为字符串
                            // (String)是类型转换操作符，将Object转换为String
                            String message = (String) obj;
                            
                            // 调用服务器的广播消息方法处理消息
                            // 通过server引用调用外部ChatServer类的broadcastMessage方法
                            server.broadcastMessage(message);
                            
                            // 检查是否收到退出消息
                            // 如果客户端发送"quit"消息，需要断开连接
                            if ("quit".equals(message)) {
                                // 设置连接状态为断开
                                // 这将导致while循环结束，停止处理该客户端
                                isConnected = false;
                                
                                // 跳出循环，结束当前客户端的处理
                                // break语句立即结束当前循环
                                break;
                            }
                            
                            // 将收到的消息进行反转处理
                            // 使用StringBuilder反转字符串
                            String reversedMessage = new StringBuilder(message).reverse().toString();
                            
                            // 将反转后的消息发送回客户端
                            // writeObject方法将对象发送到客户端
                            // 这是服务器对客户端消息的响应
                            output.writeObject(reversedMessage);
                            
                            // 刷新输出流，确保消息立即发送
                            // flush方法强制将缓冲区中的数据发送出去
                            output.flush();
                        }
                    } catch (Exception e) {
                        // 异常处理：如果读取或处理客户端数据时发生错误
                        // 跳出循环，结束当前客户端的处理
                        System.out.println("处理客户端数据时发生错误: " + e.getMessage());
                        break;
                    }
                }
            } catch (Exception e) {
                // 异常处理：如果处理客户端连接时发生其他错误
                // 但在当前代码中没有具体处理
                System.out.println("处理客户端连接时发生错误: " + e.getMessage());
            }
            
            // 关闭当前客户端连接
            // 当循环结束时，确保正确关闭客户端连接
            close();
            
            // 从服务器的客户端列表中移除当前客户端
            // 通知服务器这个客户端已经断开连接
            server.removeClient(this);
        }
        
        /**
         * 关闭客户端连接，释放相关资源
         * 正确的资源管理是网络编程中的重要实践
         */
        public void close() {
            // 设置连接状态为断开
            // 确保即使在异常情况下，也不会继续处理该客户端
            isConnected = false;
            
            try {
                // 检查并关闭对象输入流
                // 如果input不为null（已成功创建），则关闭它
                // close()方法会释放与流关联的所有系统资源
                if (input != null) input.close();
                
                // 检查并关闭对象输出流
                // 如果output不为null（已成功创建），则关闭它
                if (output != null) output.close();
                
                // 检查客户端套接字是否存在且未关闭，如果是则关闭它
                // clientSocket != null 确保套接字对象存在
                // !clientSocket.isClosed() 确保套接字尚未关闭
                if (clientSocket != null && !clientSocket.isClosed()) clientSocket.close();
            } catch (Exception e) {
                // 异常处理：如果关闭资源时发生错误
                // 但在当前代码中没有具体处理
                System.out.println("关闭客户端连接时发生错误: " + e.getMessage());
            }
        }
    }
}